#include "mainwindow.h"
#include "ui_mainwindow.h"

// Constructor for the class MainWindow
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    // Basic UI setup
    ui->setupUi(this);
    setupStatusBar();
    lay = new QVBoxLayout();
    ui->frame->setLayout(lay);
    setWindowTitle(APP_NAME);

    // Setup time to update time elapsed UI
    clock = new QTimer(this);
    clock->setInterval(1000);

    // Counters for time elapsed
    global_elapsed = new QElapsedTimer();
    local_elapsed = new QElapsedTimer();

    // Initialize variables
    difficulty_level = 1;

    temp_game_points = 0;
    game_points = 0;
    completed_points = 0;
    time_points = 0;
    skipped_points = 0;
    hint_points = 0;

    completed_games = 0;
    skipped_games = 0;
    hinted_games = 0;

    hall_of_fame_dir = gameDirectory("hall_of_fame.txt");

    // Dummy widget
    current_widget = new BaseGame();

    // Setup first game
    setWidget(0);

    // Make connections
    connect(ui->lowerlvl_button,SIGNAL(clicked()),this,SLOT(onLowerDiffLvlButtonClicked()));
    connect(ui->raiselvl_button,SIGNAL(clicked()),this,SLOT(onRaiseDiffLvlButtonClicked()));

    connect(ui->reset_button, SIGNAL(clicked()), this, SLOT(onResetButtonClicked()));
    connect(ui->hint_button,SIGNAL(clicked()),this,SLOT(onHintButtonClicked()));
    connect(ui->check_button, SIGNAL(clicked()), this, SLOT(onCheckButtonClicked()));
    connect(ui->next_button,SIGNAL(clicked()),this,SLOT(onNextButtonClicked()));
    connect(ui->skip_button,SIGNAL(clicked()),this,SLOT(onSkipButtonClicked()));

    connect(clock, SIGNAL(timeout()), this, SLOT(updateTime()));

    // Start timers
    clock->start();
    global_elapsed->start();
    local_elapsed->start();

}

// Desctructor for the class MainWindow
MainWindow::~MainWindow()
{
    delete ui;
}

// Setup UI for Statusbar
void MainWindow::setupStatusBar()
{
    // Init widgets
    points_status = new QLabel(this);
    progress_status = new QLabel(this);
    progressbar_status = new QProgressBar(this);
    progressbar_status->setMaximumSize(QSize(100,10));

    // Add widgets to statusbar
    ui->statusBar->addWidget(points_status);
    ui->statusBar->addWidget(progress_status);
    ui->statusBar->addWidget(progressbar_status);
}

// Construct the appropriate widget given the index of the game
void MainWindow::newGame(int index)
{
    if(index == 0) current_widget = new StartGame(ui->frame, difficulty_level);
    if(index == 1) current_widget = new ButtonGame(ui->frame, difficulty_level);
    if(index == 2) current_widget = new HoverGame(ui->frame, difficulty_level);
    if(index == 3) current_widget = new SliderGame(ui->frame, difficulty_level);
    if(index == 4) current_widget = new WaitGame(ui->frame, difficulty_level);
    if(index == 5) current_widget = new ProgressGame(ui->frame, difficulty_level);
    if(index == 6) current_widget = new BlackJackGame(ui->frame, difficulty_level);
    if(index == 7) current_widget = new ColorGame(ui->frame, difficulty_level);
    if(index == 8) current_widget = new RandomGame(ui->frame, difficulty_level);
    if(index == 9) current_widget = new MemoryGame(ui->frame, difficulty_level);
    if(index == 10) current_widget = new LineEditGame(ui->frame, difficulty_level);
    if(index == 11) current_widget = new CheckGame(ui->frame, difficulty_level);
    if(index == 12) current_widget = new MazeGame(ui->frame, difficulty_level);
    if(index == 13) current_widget = new EndGame(ui->frame, difficulty_level);
}

// Set current widget to be the one of index
void MainWindow::setWidget(int index)
{
    // Change index to match current
    current_index = index;
    // If index is the last game (all of them done)
    if(index == total_games()) {
        // Disable interface
        ui->hint_button->setEnabled(false);
        ui->lowerlvl_button->setEnabled(false);
        ui->raiselvl_button->setEnabled(false);
        ui->next_button->setEnabled(false);
        ui->check_button->setEnabled(false);
        ui->skip_button->setEnabled(false);
        ui->reset_button->setEnabled(false);

        // Get data for Hall Of fame
        // Cast to EndGame to acces name() member (secure)
        QString name = static_cast<EndGame*>(current_widget)->name();
        int points = total_points();
        int time = global_elapsed->elapsed();

        // Save new string to file
        saveToHallOfFame(name, points, time);

        // Disable clock
        clock->stop();

        // Display hall of fame
        displayHallOfFame();
    // Else if game is normal game
    } else {
        // Delete current widget
        delete current_widget;

        // Create the new widget and add to layout
#ifndef DEBUG_GAME
        newGame(index);
#else
        newGame(DEBUG_GAME);
#endif // DEBUG_GAME
        lay->addWidget(current_widget);

        // Disable next button (will be enable by check_button)
        ui->next_button->setEnabled(false);

        // Connect point modification to modification of points
        connect(current_widget, SIGNAL(pointsModification(int)), this, SLOT(onPointsModification(int)));

        // Save time of last game completion
        last_completed = global_elapsed->elapsed();

        // Restart clock for the local time tracking
        local_elapsed->restart();
    }

    // Update inteface
    updatePoints();
    updateGameInfo();
}

// Append results string to hall of fame file
void MainWindow::saveToHallOfFame(QString name, int points, int time)
{
    // Setup file
    QFile * f = new QFile(hall_of_fame_dir);
    // Open if possible
    if(f->open(QIODevice::Append | QIODevice::Text)) {
        // Setup text stream
        QTextStream out(f);
        // Polish characters to avoid conflict
        name = name.replace("|","");
        // Handle missing name
        if( name.isEmpty() ) name = "Nobody";
        // Write to file
        out << name << "|" << points << "|" << time << "\n";
        // Close file
        f->close();
    }
}

// Display hall of fame UI
void MainWindow::displayHallOfFame()
{
    // Delete widget
    delete current_widget;

    // Create and add widget
    HallOfFameDisplay * disp = new HallOfFameDisplay(this, hall_of_fame_dir);
    lay->addWidget(disp);
}


// Slot to reset to current widget
void MainWindow::onResetButtonClicked()
{
    // Reset temp points (keep negatives)
    temp_game_points = temp_game_points > 0 ? 0 : temp_game_points;
    game_points += temp_game_points;
    temp_game_points = 0;

    // Call setWidget with current index
    setWidget(current_index);
}

// Slot to handle hint button
void MainWindow::onHintButtonClicked()
{
    // If too many hints were used
    if(hinted_games >= total_hints()) {
        // Create and show message containing a warning
        QMessageBox * diag = new QMessageBox(this);
        diag->show();
        diag->setWindowTitle("Hints overflow");
        diag->setText("No more hint available: you used all of them.");
    // If there is some hint available
    } else {
        // Remove points for hint
        hint_points -= difficulty_level * 50;

        // Display hint
        QMessageBox * diag = new QMessageBox(this);
        diag->show();
        diag->setWindowTitle("Hint");
        diag->setText(current_widget->hint());

        // Increase counters
        hinted_games++;

        // Update UI
        updateGameInfo();
        updatePoints();
    }
}

// Slot to proceed to next widget, game completed
void MainWindow::onNextButtonClicked()
{
    // Raise points
    completed_points += difficulty_level * 100;
    int time = 1000 * 60 * 5 - local_elapsed->elapsed();
    if(time > 0) time_points += time / 60 / 10;

    // Flush obtained point to game points and reset
    game_points += temp_game_points;
    temp_game_points = 0;

    // Increase counter for completed games
    completed_games++;

    // Call next widget
    setWidget(current_index + 1);
}

// Slot to handle skip utton
void MainWindow::onSkipButtonClicked()
{
    // Reset temp points to zero
    temp_game_points = 0;

    // If too many skips are used
    if(skipped_games >= total_skip()) {
        // Show message box
        QMessageBox * diag = new QMessageBox(this);
        diag->show();
        diag->setWindowTitle("Skip overflow");
        diag->setText("No more skip available: you used all of them.");
    } else {
        // Remove pints
        skipped_points -= difficulty_level * 100;

        // Increase counter
        skipped_games++;

        // Call next widget
        setWidget(current_index + 1);
    }
}

// Slot to handle points modification by the widget
void MainWindow::onPointsModification(int diff)
{
    // Remove points for hint
    temp_game_points += diff;

    // Update UI
    updatePoints();
}

// Set diff level to diff_level
void MainWindow::setDiffLevel(int diff_level)
{
    // If valid diff level is chosen set diff level
    if(diff_level > 0 && diff_level <= 10) difficulty_level = diff_level;

    // And reset the widget
    onResetButtonClicked();
}

// Slot to raise the diff level
void MainWindow::onRaiseDiffLvlButtonClicked()
{
    // Set diff level to diff + 1
    setDiffLevel(difficulty_level + 1);
}

// Slot to lower the diff level
void MainWindow::onLowerDiffLvlButtonClicked()
{
    // Set diff level to diff - 1
    setDiffLevel(difficulty_level - 1);
}

// Check if game is completed
void MainWindow::onCheckButtonClicked()
{
    // If solved enable next
    if(current_widget->isSolved()) ui->next_button->setEnabled(true);
    // If not disable it
    else ui->next_button->setEnabled(false);
}

// Update UI for counters
void MainWindow::updateGameInfo()
{
    // Set labels
    ui->completed_label->setText(QString::number(completed_games));
    ui->todo_label->setText(QString::number(total_games() - current_index));
    ui->total_label->setText(QString::number(total_games()));


    // Set difficulty label
    ui->difficulty_label->setText(QString::number(difficulty_level));

    // Set hint labels
    ui->hintleft_label->setText(QString::number(total_hints() - hinted_games));
    ui->hintused_label->setText(QString::number(hinted_games));

    // Set skip labels
    ui->skipleft_label->setText(QString::number(total_skip() - skipped_games));
    ui->skipped_label->setText(QString::number(skipped_games));

    // If no display results
    if( current_index != total_games() ) {
        // Update x of y
        QString str("Game %1 of %2:");
        ui->title_label->setText(str.arg(current_index + 1).arg(total_games()));
        str = QString("| %1 of %2 |");
        progress_status->setText(str.arg(current_index + 1).arg(total_games()));
    // If display results
    } else {
        ui->title_label->setText("Hall of Fame:");
        progress_status->setText("|");
    }

    // Set progress bar
    float perc = ((float) current_index) / ((float) total_games()) * 100;
    ui->progress_bar->setValue((int) perc);
    progressbar_status->setValue((int) perc);
}

// Update points UI
void MainWindow::updatePoints()
{
    // LCD display
    ui->points_display->display(total_points());

    // Labels
    QString str;
    str = QString("%1 points for completed games");
    ui->comppoints_label->setText(str.arg(completed_points));
    str = QString("%1 points from time");
    ui->timepoints_label->setText(str.arg(time_points));
    str = QString("%1 points for skipped games");
    ui->skippoints_label->setText(str.arg(skipped_points));
    str = QString("%1 points spent on hints");
    ui->hintpoints_label->setText(str.arg(hint_points));
    str = QString("%1 points from games");
    ui->gamepoints_label->setText(str.arg(game_points + temp_game_points));

    // Statusbar
    str = QString("%1 points");
    points_status->setText(str.arg(total_points()));
}

// Update time UI
void MainWindow::updateTime()
{
    // Get times and mean
    QTime time = QTime(0,0,0,0);
    int glob = global_elapsed->elapsed();
    QString str1 = time.addMSecs(glob).toString();
    int loc = local_elapsed->elapsed();
    QString str2 = time.addMSecs(loc).toString();
    int mean;
    if(completed_games != 0) mean = last_completed / completed_games; else mean = glob;
    QString str3 = time.addMSecs(mean).toString();

    // Set labels
    ui->totaltime_label->setText(str1);
    ui->localtime_label->setText(str2);
    ui->meantime_label->setText(str3);
}
